{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<font color=\"red\">注</font>: 使用 tensorboard 可视化需要安装 tensorflow (TensorBoard依赖于tensorflow库，可以任意安装tensorflow的gpu/cpu版本)\n",
    "\n",
    "```shell\n",
    "pip install tensorflow-cpu\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-04-30T03:08:26.466719400Z",
     "start_time": "2024-04-30T03:08:17.277867200Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-03T11:57:50.479112Z",
     "iopub.status.busy": "2025-02-03T11:57:50.478411Z",
     "iopub.status.idle": "2025-02-03T11:57:55.119372Z",
     "shell.execute_reply": "2025-02-03T11:57:55.118413Z",
     "shell.execute_reply.started": "2025-02-03T11:57:50.479041Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/usr/local/lib/python3.10/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
      "  from .autonotebook import tqdm as notebook_tqdm\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "sys.version_info(major=3, minor=10, micro=14, releaselevel='final', serial=0)\n",
      "matplotlib 3.10.0\n",
      "numpy 1.26.4\n",
      "pandas 2.2.3\n",
      "sklearn 1.6.0\n",
      "torch 2.5.1+cu124\n",
      "cuda:0\n"
     ]
    }
   ],
   "source": [
    "import matplotlib as mpl\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline\n",
    "import numpy as np\n",
    "import sklearn\n",
    "import pandas as pd\n",
    "import os\n",
    "import sys\n",
    "import time\n",
    "from tqdm.auto import tqdm\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "\n",
    "print(sys.version_info)\n",
    "for module in mpl, np, pd, sklearn, torch:\n",
    "    print(module.__name__, module.__version__)\n",
    "    \n",
    "device = torch.device(\"cuda:0\") if torch.cuda.is_available() else torch.device(\"cpu\")\n",
    "print(device)\n",
    "\n",
    "seed = 42\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "jp-MarkdownHeadingCollapsed": true,
    "tags": []
   },
   "source": [
    "## 数据准备\n",
    "\n",
    "https://www.kaggle.com/competitions/cifar-10/data\n",
    "\n",
    "```shell\n",
    "$ tree -L 1 cifar-10                                    \n",
    "cifar-10\n",
    "├── sampleSubmission.csv\n",
    "├── test\n",
    "├── train\n",
    "└── trainLabels.csv\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-02-03T11:58:30.060527Z",
     "iopub.status.busy": "2025-02-03T11:58:30.059884Z",
     "iopub.status.idle": "2025-02-03T11:58:34.118891Z",
     "shell.execute_reply": "2025-02-03T11:58:34.117993Z",
     "shell.execute_reply.started": "2025-02-03T11:58:30.060481Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[(PosixPath('competitions/cifar-10/train/1.png'), 'frog'),\n",
      " (PosixPath('competitions/cifar-10/train/2.png'), 'truck'),\n",
      " (PosixPath('competitions/cifar-10/train/3.png'), 'truck'),\n",
      " (PosixPath('competitions/cifar-10/train/4.png'), 'deer'),\n",
      " (PosixPath('competitions/cifar-10/train/5.png'), 'automobile')]\n",
      "[(PosixPath('competitions/cifar-10/test/1.png'), 'cat'),\n",
      " (PosixPath('competitions/cifar-10/test/2.png'), 'cat'),\n",
      " (PosixPath('competitions/cifar-10/test/3.png'), 'cat'),\n",
      " (PosixPath('competitions/cifar-10/test/4.png'), 'cat'),\n",
      " (PosixPath('competitions/cifar-10/test/5.png'), 'cat')]\n",
      "50000 300000\n"
     ]
    }
   ],
   "source": [
    "from pathlib import Path\n",
    "\n",
    "DATA_DIR = Path(\".\")\n",
    "DATA_DIR1 =Path(\"competitions/cifar-10/\")\n",
    "train_lables_file = DATA_DIR / \"trainLabels.csv\"\n",
    "test_csv_file = DATA_DIR / \"sampleSubmission.csv\" #测试集模板csv文件\n",
    "train_folder = DATA_DIR1 / \"train\"\n",
    "test_folder = DATA_DIR1 / \"test\"\n",
    "\n",
    "#所有的类别\n",
    "class_names = [\n",
    "    'airplane',\n",
    "    'automobile',\n",
    "    'bird',\n",
    "    'cat',\n",
    "    'deer',\n",
    "    'dog',\n",
    "    'frog',\n",
    "    'horse',\n",
    "    'ship',\n",
    "    'truck',\n",
    "]\n",
    "\n",
    "def parse_csv_file(filepath, folder):\n",
    "    \"\"\"Parses csv files into (filename(path), label) format\"\"\"\n",
    "    results = []\n",
    "    #读取所有行\n",
    "    with open(filepath, 'r') as f:\n",
    "#         lines = f.readlines()  为什么加[1:]，可以试这个\n",
    "        #第一行不需要，因为第一行是标签\n",
    "        lines = f.readlines()[1:] \n",
    "    for line in lines:#依次去取每一行\n",
    "        image_id, label_str = line.strip('\\n').split(',')\n",
    "        image_full_path = folder / f\"{image_id}.png\"\n",
    "        results.append((image_full_path, label_str)) #得到对应图片的路径和分类\n",
    "    return results\n",
    "\n",
    "#解析对应的文件夹\n",
    "train_labels_info = parse_csv_file(train_lables_file, train_folder)\n",
    "test_csv_info = parse_csv_file(test_csv_file, test_folder)\n",
    "#打印\n",
    "import pprint\n",
    "pprint.pprint(train_labels_info[0:5])\n",
    "pprint.pprint(test_csv_info[0:5])\n",
    "print(len(train_labels_info), len(test_csv_info))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-04-30T03:09:30.583944400Z",
     "start_time": "2024-04-30T03:09:30.436028300Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-03T11:58:36.809420Z",
     "iopub.status.busy": "2025-02-03T11:58:36.808677Z",
     "iopub.status.idle": "2025-02-03T11:58:36.946506Z",
     "shell.execute_reply": "2025-02-03T11:58:36.945368Z",
     "shell.execute_reply.started": "2025-02-03T11:58:36.809360Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "                            filepath       class\n",
      "0  competitions/cifar-10/train/1.png        frog\n",
      "1  competitions/cifar-10/train/2.png       truck\n",
      "2  competitions/cifar-10/train/3.png       truck\n",
      "3  competitions/cifar-10/train/4.png        deer\n",
      "4  competitions/cifar-10/train/5.png  automobile\n",
      "                                filepath       class\n",
      "0  competitions/cifar-10/train/45001.png       horse\n",
      "1  competitions/cifar-10/train/45002.png  automobile\n",
      "2  competitions/cifar-10/train/45003.png        deer\n",
      "3  competitions/cifar-10/train/45004.png  automobile\n",
      "4  competitions/cifar-10/train/45005.png    airplane\n",
      "                           filepath class\n",
      "0  competitions/cifar-10/test/1.png   cat\n",
      "1  competitions/cifar-10/test/2.png   cat\n",
      "2  competitions/cifar-10/test/3.png   cat\n",
      "3  competitions/cifar-10/test/4.png   cat\n",
      "4  competitions/cifar-10/test/5.png   cat\n"
     ]
    }
   ],
   "source": [
    "# train_df = pd.DataFrame(train_labels_info)\n",
    "train_df = pd.DataFrame(train_labels_info[0:45000])\n",
    "valid_df = pd.DataFrame(train_labels_info[45000:])\n",
    "test_df = pd.DataFrame(test_csv_info)\n",
    "\n",
    "train_df.columns = ['filepath', 'class']\n",
    "valid_df.columns = ['filepath', 'class']\n",
    "test_df.columns = ['filepath', 'class']\n",
    "\n",
    "print(train_df.head())\n",
    "print(valid_df.head())\n",
    "print(test_df.head())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-04-30T03:09:40.361166200Z",
     "start_time": "2024-04-30T03:09:38.275234700Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-03T11:58:41.274145Z",
     "iopub.status.busy": "2025-02-03T11:58:41.273625Z",
     "iopub.status.idle": "2025-02-03T11:58:43.123868Z",
     "shell.execute_reply": "2025-02-03T11:58:43.122526Z",
     "shell.execute_reply.started": "2025-02-03T11:58:41.274113Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "from PIL import Image\n",
    "from torch.utils.data import Dataset, DataLoader\n",
    "from torchvision import transforms\n",
    "\n",
    "class Cifar10Dataset(Dataset):\n",
    "    df_map = {\n",
    "        \"train\": train_df,\n",
    "        \"eval\": valid_df,\n",
    "        \"test\": test_df\n",
    "    }\n",
    "    label_to_idx = {label: idx for idx, label in enumerate(class_names)}\n",
    "    idx_to_label = {idx: label for idx, label in enumerate(class_names)}\n",
    "    def __init__(self, mode, transform=None):\n",
    "        self.df = self.df_map.get(mode, None)\n",
    "        if self.df is None:\n",
    "            raise ValueError(\"mode should be one of train, val, test, but got {}\".format(mode))\n",
    "\n",
    "        self.transform = transform\n",
    "        \n",
    "    def __getitem__(self, index):\n",
    "        img_path, label = self.df.iloc[index]\n",
    "        img = Image.open(img_path).convert('RGB')\n",
    "        # # img 转换为 channel first\n",
    "        # img = img.transpose((2, 0, 1))\n",
    "        # transform\n",
    "        img = self.transform(img)\n",
    "        # label 转换为 idx\n",
    "        label = self.label_to_idx[label]\n",
    "        return img, label\n",
    "    \n",
    "    def __len__(self):\n",
    "        return self.df.shape[0]\n",
    "    \n",
    "IMAGE_SIZE = 32\n",
    "mean, std = [0.4914, 0.4822, 0.4465], [0.247, 0.243, 0.261]\n",
    "\n",
    "transforms_train = transforms.Compose([\n",
    "        # resize\n",
    "        transforms.Resize((IMAGE_SIZE, IMAGE_SIZE)),\n",
    "        # random rotation 40\n",
    "        transforms.RandomRotation(40),\n",
    "        # horizaontal flip\n",
    "        transforms.RandomHorizontalFlip(),\n",
    "        transforms.ToTensor(),\n",
    "        transforms.Normalize(mean, std)\n",
    "    ])\n",
    "\n",
    "transforms_eval = transforms.Compose([\n",
    "        # resize\n",
    "        transforms.Resize((IMAGE_SIZE, IMAGE_SIZE)),\n",
    "        transforms.ToTensor(),\n",
    "        transforms.Normalize(mean, std)\n",
    "    ])\n",
    "\n",
    "train_ds = Cifar10Dataset(\"train\", transforms_train)\n",
    "eval_ds = Cifar10Dataset(\"eval\", transforms_eval) "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-04-30T03:09:43.545874100Z",
     "start_time": "2024-04-30T03:09:43.519888900Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-03T11:58:43.716924Z",
     "iopub.status.busy": "2025-02-03T11:58:43.715907Z",
     "iopub.status.idle": "2025-02-03T11:58:43.724397Z",
     "shell.execute_reply": "2025-02-03T11:58:43.722896Z",
     "shell.execute_reply.started": "2025-02-03T11:58:43.716840Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "batch_size = 64\n",
    "train_dl = DataLoader(train_ds, batch_size=batch_size, shuffle=True, num_workers=4)   \n",
    "eval_dl = DataLoader(eval_ds, batch_size=batch_size, shuffle=False, num_workers=4)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(tensor([-0.2207, -0.2280, -0.1984]), tensor([0.9980, 0.9955, 0.9041]))\n"
     ]
    }
   ],
   "source": [
    "# 遍历train_ds得到每张图片，计算每个通道的均值和方差\n",
    "# def cal_mean_std(ds):\n",
    "#     mean = 0.\n",
    "#     std = 0.\n",
    "#     for img, _ in ds:\n",
    "#         mean += img.mean(dim=(1, 2))\n",
    "#         std += img.std(dim=(1, 2))\n",
    "#     mean /= len(ds)\n",
    "#     std /= len(ds)\n",
    "#     return mean, std\n",
    "#\n",
    "# # 经过 normalize 后 均值为0，方差为1\n",
    "# print(cal_mean_std(train_ds))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 定义模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-04-30T03:33:59.691021200Z",
     "start_time": "2024-04-30T03:33:59.642042400Z"
    },
    "ExecutionIndicator": {
     "show": true
    },
    "execution": {
     "iopub.execute_input": "2025-02-03T12:21:38.259564Z",
     "iopub.status.busy": "2025-02-03T12:21:38.258981Z",
     "iopub.status.idle": "2025-02-03T12:21:38.274856Z",
     "shell.execute_reply": "2025-02-03T12:21:38.274187Z",
     "shell.execute_reply.started": "2025-02-03T12:21:38.259519Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "             model.0.weight             paramerters num: 864\n",
      "              model.0.bias              paramerters num: 32\n",
      "             model.2.weight             paramerters num: 9216\n",
      "              model.2.bias              paramerters num: 32\n",
      "             model.5.weight             paramerters num: 9216\n",
      "              model.5.bias              paramerters num: 32\n",
      "             model.7.weight             paramerters num: 9216\n",
      "              model.7.bias              paramerters num: 32\n",
      "            model.10.weight             paramerters num: 9216\n",
      "             model.10.bias              paramerters num: 32\n",
      "            model.12.weight             paramerters num: 9216\n",
      "             model.12.bias              paramerters num: 32\n",
      "            model.16.weight             paramerters num: 5120\n",
      "             model.16.bias              paramerters num: 10\n"
     ]
    }
   ],
   "source": [
    "class VGG(nn.Module):\n",
    "    def __init__(self, num_classes):\n",
    "        super().__init__()\n",
    "        self.model = nn.Sequential(\n",
    "            nn.Conv2d(in_channels=3, out_channels=32, kernel_size=3, padding=\"same\"),\n",
    "            nn.ReLU(),\n",
    "            nn.Conv2d(in_channels=32, out_channels=32, kernel_size=3, padding=\"same\"),\n",
    "            nn.ReLU(),\n",
    "            nn.MaxPool2d(kernel_size=2),\n",
    "            nn.Conv2d(in_channels=32, out_channels=32, kernel_size=3, padding=\"same\"),\n",
    "            nn.ReLU(),\n",
    "            nn.Conv2d(in_channels=32, out_channels=32, kernel_size=3, padding=\"same\"),\n",
    "            nn.ReLU(),\n",
    "            nn.MaxPool2d(kernel_size=2),\n",
    "            nn.Conv2d(in_channels=32, out_channels=32, kernel_size=3, padding=\"same\"),\n",
    "            nn.ReLU(),\n",
    "            nn.Conv2d(in_channels=32, out_channels=32, kernel_size=3, padding=\"same\"),\n",
    "            nn.ReLU(),\n",
    "            nn.MaxPool2d(kernel_size=2),\n",
    "            nn.Flatten(),\n",
    "            nn.Linear(512, num_classes),\n",
    "        )\n",
    "        self.init_weights()\n",
    "        \n",
    "    def init_weights(self):\n",
    "        \"\"\"使用 xavier 均匀分布来初始化全连接层、卷积层的权重 W\"\"\"\n",
    "        for m in self.modules():\n",
    "            if isinstance(m, (nn.Linear, nn.Conv2d)): # 全连接层、卷积层\n",
    "                # 采用kaiming_uniform初始化权重, mode='fan_in'表示权重初始化为均匀分布，nonlinearity='relu'表示激活函数为relu\n",
    "                # nn.init.kaiming_uniform_(m.weight, mode='fan_in', nonlinearity='relu')  \n",
    "                # nn.init.kaiming_uniform_(m.weight, mode='fan_out', nonlinearity='relu')  \n",
    "                nn.init.xavier_uniform_(m.weight)\n",
    "                if m.bias is not None:\n",
    "                    nn.init.constant_(m.bias, 0.01)  # 偏置初始化为常数值，比如0.01\n",
    "        \n",
    "    def forward(self, x):\n",
    "        return self.model(x)\n",
    "        \n",
    "for key, value in VGG(len(class_names)).named_parameters():\n",
    "    print(f\"{key:^40}paramerters num: {np.prod(value.shape)}\")\n",
    "    \n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 训练\n",
    "\n",
    "pytorch的训练需要自行实现，包括\n",
    "1. 定义损失函数\n",
    "2. 定义优化器\n",
    "3. 定义训练步\n",
    "4. 训练"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-02-03T11:59:05.954586Z",
     "iopub.status.busy": "2025-02-03T11:59:05.953927Z",
     "iopub.status.idle": "2025-02-03T11:59:06.015382Z",
     "shell.execute_reply": "2025-02-03T11:59:06.014025Z",
     "shell.execute_reply.started": "2025-02-03T11:59:05.954542Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "from sklearn.metrics import accuracy_score\n",
    "\n",
    "@torch.no_grad()\n",
    "def evaluating(model, dataloader, loss_fct):\n",
    "    loss_list = []\n",
    "    pred_list = []\n",
    "    label_list = []\n",
    "    for datas, labels in dataloader:\n",
    "        datas = datas.to(device)\n",
    "        labels = labels.to(device)\n",
    "        # 前向计算\n",
    "        logits = model(datas)\n",
    "        loss = loss_fct(logits, labels)         # 验证集损失\n",
    "        loss_list.append(loss.item())\n",
    "        \n",
    "        preds = logits.argmax(axis=-1)    # 验证集预测\n",
    "        pred_list.extend(preds.cpu().numpy().tolist())\n",
    "        label_list.extend(labels.cpu().numpy().tolist())\n",
    "        \n",
    "    acc = accuracy_score(label_list, pred_list)\n",
    "    return np.mean(loss_list), acc\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "jp-MarkdownHeadingCollapsed": true,
    "tags": []
   },
   "source": [
    "### TensorBoard 可视化\n",
    "\n",
    "\n",
    "训练过程中可以使用如下命令启动tensorboard服务。\n",
    "\n",
    "```shell\n",
    "tensorboard \\\n",
    "    --logdir=runs \\     # log 存放路径\n",
    "    --host 0.0.0.0 \\    # ip\n",
    "    --port 8848         # 端口\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-02-03T11:59:10.362921Z",
     "iopub.status.busy": "2025-02-03T11:59:10.361710Z",
     "iopub.status.idle": "2025-02-03T11:59:10.498764Z",
     "shell.execute_reply": "2025-02-03T11:59:10.497381Z",
     "shell.execute_reply.started": "2025-02-03T11:59:10.362860Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "from torch.utils.tensorboard import SummaryWriter\n",
    "\n",
    "\n",
    "class TensorBoardCallback:\n",
    "    def __init__(self, log_dir, flush_secs=10):\n",
    "        \"\"\"\n",
    "        Args:\n",
    "            log_dir (str): dir to write log.\n",
    "            flush_secs (int, optional): write to dsk each flush_secs seconds. Defaults to 10.\n",
    "        \"\"\"\n",
    "        self.writer = SummaryWriter(log_dir=log_dir, flush_secs=flush_secs)\n",
    "\n",
    "    def draw_model(self, model, input_shape):\n",
    "        self.writer.add_graph(model, input_to_model=torch.randn(input_shape))\n",
    "        \n",
    "    def add_loss_scalars(self, step, loss, val_loss):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/loss\", \n",
    "            tag_scalar_dict={\"loss\": loss, \"val_loss\": val_loss},\n",
    "            global_step=step,\n",
    "            )\n",
    "        \n",
    "    def add_acc_scalars(self, step, acc, val_acc):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/accuracy\",\n",
    "            tag_scalar_dict={\"accuracy\": acc, \"val_accuracy\": val_acc},\n",
    "            global_step=step,\n",
    "        )\n",
    "        \n",
    "    def add_lr_scalars(self, step, learning_rate):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/learning_rate\",\n",
    "            tag_scalar_dict={\"learning_rate\": learning_rate},\n",
    "            global_step=step,\n",
    "            \n",
    "        )\n",
    "    \n",
    "    def __call__(self, step, **kwargs):\n",
    "        # add loss\n",
    "        loss = kwargs.pop(\"loss\", None)\n",
    "        val_loss = kwargs.pop(\"val_loss\", None)\n",
    "        if loss is not None and val_loss is not None:\n",
    "            self.add_loss_scalars(step, loss, val_loss)\n",
    "        # add acc\n",
    "        acc = kwargs.pop(\"acc\", None)\n",
    "        val_acc = kwargs.pop(\"val_acc\", None)\n",
    "        if acc is not None and val_acc is not None:\n",
    "            self.add_acc_scalars(step, acc, val_acc)\n",
    "        # add lr\n",
    "        learning_rate = kwargs.pop(\"lr\", None)\n",
    "        if learning_rate is not None:\n",
    "            self.add_lr_scalars(step, learning_rate)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "jp-MarkdownHeadingCollapsed": true,
    "tags": []
   },
   "source": [
    "### Save Best\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-02-03T11:59:13.158925Z",
     "iopub.status.busy": "2025-02-03T11:59:13.158049Z",
     "iopub.status.idle": "2025-02-03T11:59:13.168078Z",
     "shell.execute_reply": "2025-02-03T11:59:13.167109Z",
     "shell.execute_reply.started": "2025-02-03T11:59:13.158877Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "class SaveCheckpointsCallback:\n",
    "    def __init__(self, save_dir, save_step=5000, save_best_only=True):\n",
    "        \"\"\"\n",
    "        Save checkpoints each save_epoch epoch. \n",
    "        We save checkpoint by epoch in this implementation.\n",
    "        Usually, training scripts with pytorch evaluating model and save checkpoint by step.\n",
    "\n",
    "        Args:\n",
    "            save_dir (str): dir to save checkpoint\n",
    "            save_epoch (int, optional): the frequency to save checkpoint. Defaults to 1.\n",
    "            save_best_only (bool, optional): If True, only save the best model or save each model at every epoch.\n",
    "        \"\"\"\n",
    "        self.save_dir = save_dir\n",
    "        self.save_step = save_step\n",
    "        self.save_best_only = save_best_only\n",
    "        self.best_metrics = -1\n",
    "        \n",
    "        # mkdir\n",
    "        if not os.path.exists(self.save_dir):\n",
    "            os.mkdir(self.save_dir)\n",
    "        \n",
    "    def __call__(self, step, state_dict, metric=None):\n",
    "        if step % self.save_step > 0:\n",
    "            return\n",
    "        \n",
    "        if self.save_best_only:\n",
    "            assert metric is not None\n",
    "            if metric >= self.best_metrics:\n",
    "                # save checkpoints\n",
    "                torch.save(state_dict, os.path.join(self.save_dir, \"best.ckpt\"))\n",
    "                # update best metrics\n",
    "                self.best_metrics = metric\n",
    "        else:\n",
    "            torch.save(state_dict, os.path.join(self.save_dir, f\"{step}.ckpt\"))\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "jp-MarkdownHeadingCollapsed": true,
    "tags": []
   },
   "source": [
    "### Early Stop"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-02-03T11:59:17.884479Z",
     "iopub.status.busy": "2025-02-03T11:59:17.883822Z",
     "iopub.status.idle": "2025-02-03T11:59:17.893866Z",
     "shell.execute_reply": "2025-02-03T11:59:17.892627Z",
     "shell.execute_reply.started": "2025-02-03T11:59:17.884429Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "class EarlyStopCallback:\n",
    "    def __init__(self, patience=5, min_delta=0.01):\n",
    "        \"\"\"\n",
    "\n",
    "        Args:\n",
    "            patience (int, optional): Number of epochs with no improvement after which training will be stopped.. Defaults to 5.\n",
    "            min_delta (float, optional): Minimum change in the monitored quantity to qualify as an improvement, i.e. an absolute \n",
    "                change of less than min_delta, will count as no improvement. Defaults to 0.01.\n",
    "        \"\"\"\n",
    "        self.patience = patience\n",
    "        self.min_delta = min_delta\n",
    "        self.best_metric = -1\n",
    "        self.counter = 0\n",
    "        \n",
    "    def __call__(self, metric):\n",
    "        if metric >= self.best_metric + self.min_delta:\n",
    "            # update best metric\n",
    "            self.best_metric = metric\n",
    "            # reset counter \n",
    "            self.counter = 0\n",
    "        else: \n",
    "            self.counter += 1\n",
    "            \n",
    "    @property\n",
    "    def early_stop(self):\n",
    "        return self.counter >= self.patience\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## training"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "ExecutionIndicator": {
     "show": true
    },
    "execution": {
     "iopub.execute_input": "2025-02-03T12:21:40.505697Z",
     "iopub.status.busy": "2025-02-03T12:21:40.505136Z",
     "iopub.status.idle": "2025-02-03T12:23:30.483179Z",
     "shell.execute_reply": "2025-02-03T12:23:30.482211Z",
     "shell.execute_reply.started": "2025-02-03T12:21:40.505661Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 7040/7040 [01:49<00:00, 64.09it/s, epoch=9]\n"
     ]
    }
   ],
   "source": [
    "# 训练\n",
    "def training(\n",
    "    model, \n",
    "    train_loader, \n",
    "    val_loader, \n",
    "    epoch, \n",
    "    loss_fct, \n",
    "    optimizer, \n",
    "    tensorboard_callback=None,\n",
    "    save_ckpt_callback=None,\n",
    "    early_stop_callback=None,\n",
    "    eval_step=500,\n",
    "    ):\n",
    "    record_dict = {\n",
    "        \"train\": [],\n",
    "        \"val\": []\n",
    "    }\n",
    "    \n",
    "    global_step = 0\n",
    "    model.train()\n",
    "    with tqdm(total=epoch * len(train_loader)) as pbar:\n",
    "        for epoch_id in range(epoch):\n",
    "            # training\n",
    "            for datas, labels in train_loader:\n",
    "                datas = datas.to(device)\n",
    "                labels = labels.to(device)\n",
    "                # 梯度清空\n",
    "                optimizer.zero_grad()\n",
    "                # 模型前向计算\n",
    "                logits = model(datas)\n",
    "                # 计算损失\n",
    "                loss = loss_fct(logits, labels)\n",
    "                # 梯度回传\n",
    "                loss.backward()\n",
    "                # 调整优化器，包括学习率的变动等\n",
    "                optimizer.step()\n",
    "                preds = logits.argmax(axis=-1)\n",
    "            \n",
    "                acc = accuracy_score(labels.cpu().numpy(), preds.cpu().numpy())    \n",
    "                loss = loss.cpu().item()\n",
    "                # record\n",
    "                \n",
    "                record_dict[\"train\"].append({\n",
    "                    \"loss\": loss, \"acc\": acc, \"step\": global_step\n",
    "                })\n",
    "                \n",
    "                # evaluating\n",
    "                if global_step % eval_step == 0:\n",
    "                    model.eval()\n",
    "                    val_loss, val_acc = evaluating(model, val_loader, loss_fct)\n",
    "                    record_dict[\"val\"].append({\n",
    "                        \"loss\": val_loss, \"acc\": val_acc, \"step\": global_step\n",
    "                    })\n",
    "                    model.train()\n",
    "                    \n",
    "                    # 1. 使用 tensorboard 可视化\n",
    "                    if tensorboard_callback is not None:\n",
    "                        tensorboard_callback(\n",
    "                            global_step, \n",
    "                            loss=loss, val_loss=val_loss,\n",
    "                            acc=acc, val_acc=val_acc,\n",
    "                            lr=optimizer.param_groups[0][\"lr\"],\n",
    "                            )\n",
    "                \n",
    "                    # 2. 保存模型权重 save model checkpoint\n",
    "                    if save_ckpt_callback is not None:\n",
    "                        save_ckpt_callback(global_step, model.state_dict(), metric=val_acc)\n",
    "\n",
    "                    # 3. 早停 Early Stop\n",
    "                    if early_stop_callback is not None:\n",
    "                        early_stop_callback(val_acc)\n",
    "                        if early_stop_callback.early_stop:\n",
    "                            print(f\"Early stop at epoch {epoch_id} / global_step {global_step}\")\n",
    "                            return record_dict\n",
    "                    \n",
    "                # udate step\n",
    "                global_step += 1\n",
    "                pbar.update(1)\n",
    "                pbar.set_postfix({\"epoch\": epoch_id})\n",
    "        \n",
    "    return record_dict\n",
    "        \n",
    "\n",
    "epoch = 10\n",
    "\n",
    "model = VGG(num_classes=10)\n",
    "\n",
    "# 1. 定义损失函数 采用交叉熵损失\n",
    "loss_fct = nn.CrossEntropyLoss()\n",
    "# 2. 定义优化器 采用 adam\n",
    "# Optimizers specified in the torch.optim package,可以修改beta1,beta2,weight_decay等参数\n",
    "optimizer = torch.optim.Adam(model.parameters(), lr=0.001,betas=(0.9,0.999))\n",
    "\n",
    "# 1. tensorboard 可视化\n",
    "if not os.path.exists(\"runs\"):\n",
    "    os.mkdir(\"runs\")\n",
    "tensorboard_callback = TensorBoardCallback(\"runs/vgg\")\n",
    "tensorboard_callback.draw_model(model, [1, 3, IMAGE_SIZE, IMAGE_SIZE])\n",
    "# 2. save best\n",
    "if not os.path.exists(\"checkpoints\"):\n",
    "    os.makedirs(\"checkpoints\")\n",
    "save_ckpt_callback = SaveCheckpointsCallback(\"checkpoints/vgg\", save_step=len(train_dl), save_best_only=True)\n",
    "# 3. early stop\n",
    "early_stop_callback = EarlyStopCallback(patience=5)\n",
    "\n",
    "model = model.to(device)\n",
    "record = training(\n",
    "    model, \n",
    "    train_dl, \n",
    "    eval_dl, \n",
    "    epoch, \n",
    "    loss_fct, \n",
    "    optimizer, \n",
    "    tensorboard_callback=tensorboard_callback,\n",
    "    save_ckpt_callback=save_ckpt_callback,\n",
    "    early_stop_callback=early_stop_callback,\n",
    "    eval_step=len(train_dl)\n",
    "    )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-02-03T12:20:15.569211Z",
     "iopub.status.busy": "2025-02-03T12:20:15.568676Z",
     "iopub.status.idle": "2025-02-03T12:20:15.890360Z",
     "shell.execute_reply": "2025-02-03T12:20:15.889450Z",
     "shell.execute_reply.started": "2025-02-03T12:20:15.569155Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAy8AAAHACAYAAACroFdOAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAPYQAAD2EBqD+naQAA351JREFUeJzs3Xt8W3X9+PHXybVN75et7e73+w02wI2L3LaxCSIqIqLAVBRhik4E9lW5KUxFEZSb+OPmBUQUFGXAxnDA2ICxMS5jF3a/tV3vSZs2998fJ+fk0qRN0utp38/HY4+taU7ySdos55335aOEQqEQQgghhBBCCNHPmfp6AUIIIYQQQgiRCglehBBCCCGEEIYgwYsQQgghhBDCECR4EUIIIYQQQhiCBC9CCCGEEEIIQ5DgRQghhBBCCGEIErwIIYQQQgghDEGCFyGEEEIIIYQhWHr7DoPBIMeOHSMvLw9FUXr77oUQYtAKhUK4XC6GDRuGySSfXWnkfUkIIfpOuu9NvR68HDt2jJEjR/b23QohhAg7fPgwI0aM6Otl9BvyviSEEH0v1femXg9e8vLyAHWB+fn5aR/v8/lYs2YNixYtwmq1dvfyeoQR1wyy7t5mxHUbcc0weNftdDoZOXKk/v+wUA3G9yWQdfc2I67biGsGWXdv6+33pl4PXrSUfH5+fsZvEg6Hg/z8fMP8YI24ZpB19zYjrtuIawZZt5RGxRqM70sg6+5tRly3EdcMsu7e1tvvTVL0LIQQQgghhDAECV6EEEIIIYQQhiDBixBCCCGEEMIQer3nRQjRP4VCIfx+P4FAIOVjfD4fFouFtra2tI7rawN13WazGYvFIj0tPaCj18dA/X3qr9Jdt7wuhBhY0gpeAoEAt956K3/5y1+oqqpi2LBhXHnllfzkJz+R/xSEMDCv10tlZSVutzut40KhEOXl5Rw+fNhQ/wcM5HU7HA4qKiqw2Wy9vLqBq7PXx0D+feqPMlm3vC6EGDjSCl5++ctf8uCDD/LEE08wffp03n33XZYtW0ZBQQHf+973emqNQogeFAwG2b9/P2azmWHDhmGz2VI+IQgGgzQ3N5Obm2uoTQ8H4rpDoRBer5eamhr279/PxIkTDfXY+qtUXh8D8fepP0tn3fK6EGLgSSt42bhxIxdeeCGf+cxnABgzZgxPPfUU77zzTo8sTgjR87xeL8FgkJEjR+JwONI6NhgM4vV6ycrKMtQJwUBdd3Z2NlarlYMHD+rXE12TyutjoP4+9VfprlteF0IMLGkFLwsWLODhhx9m9+7dTJo0iffff58NGzZw9913Jz3G4/Hg8Xj0r51OJ6DWrPp8vrQXrB2TybF9xYhrBll3b+urdft8PkKhEKCeFKRDOy4UCqV9bF8a6OsOhUL4fD7MZnPM5UZ7TfQnRjq5F+3Jz0+IgSOt4OWmm27C6XQyZcoUzGYzgUCAO+64g8suuyzpMatWreK2225rd/maNWvS/pQ32tq1azM+tq8Ycc0g6+5tvb1ui8VCeXk5zc3NeL3ejG7D5XJ186p6x0Bct9frpbW1lddffx2/3x/zvXR7moQQQoj+Jq3g5e9//zt//etfefLJJ5k+fTrbtm3j+9//PsOGDeOKK65IeMzKlStZsWKF/rXT6WTkyJEsWrQo452M165dy8KFCw2z+6gR1wyy7t7WV+tua2vj8OHD5Obmpl1OEQqFcLlc5OXlGa7hd6Cuu62tjezsbM4444x2P08t8y2EEEIYVVrBy49+9CNuuukmvvzlLwMwc+ZMDh48yKpVq5IGL3a7Hbvd3u5yq9XapRO0rh7fF4y4ZpB197beXncgEEBRFEwmU9qlFVrpkna8USRa95gxY/j+97/P97///S7f/vr16znrrLNoaGigsLCwy7enSeX5NplMKIqS8PfIiK8H0T905+tDCCG6Iq3gxe12t3vDNJvNhqoZF0IMHGeeeSZz5szhnnvu6fJtbd68mZycnK4vSoh+Ql4fQoiBKK3g5YILLuCOO+5g1KhRTJ8+nffee4+7776br3/96z21PiGEyFgoFCIQCGCxdP5f3ZAhQ3phRUL0H9rGm6mQ14cQor9Iq87j97//PV/84he55pprmDp1Ktdffz3f/va3+dnPftZT64vx3HtHOP++jTx/0DjlKUIYTSgUwu31p/yn1RtI6/od/dGmaaXiyiuv5LXXXuPee+9FURQUReHxxx9HURRefPFF5s6di91uZ8OGDezdu5cLL7yQsrIycnNzOeWUU1i/fn3M7Y0ZMybmE2pFUfh//+//cdFFF+FwOJg4cSLPP/98xs/rP//5T6ZPn47dbmfMmDH85je/ifn+Aw88wMSJE8nKyqKsrIwvfvGL+vf+8Y9/MHPmTHJychg3bhyLFi2ipaUl47WIrkn0GunO10F3vEZSeX1kZ2fz1ltvtXt9nHTSSbzyyisxt9edr49AIMA3vvENxo4dS3Z2NpMnT+bee+9td71HH31Uf81UVFSwfPly/XtNTU1cffXVlJWVkZWVxYwZM/jvf/+b0v0LIeC/Hxxj2WPv0OQ23hTKtDIveXl53HPPPd2Sgs5Eo9vHrupmckr65O6FGBRafQGm3fxyn9z3x7cvxmFL7b+le++9l927dzNjxgxuv/12ALZv3w6okxF//etfM27cOIqKijh8+DBLly7ljjvuwG6388QTT3DppZeyY8cOxowZk/Q+brvtNn71q19x11138fvf/57LLruMgwcPUlxcnNbj2rJlC1/60pe49dZbueSSS9i4cSPXXHMNJSUlXHnllbz77rt873vf489//jMLFiygvr6eN954A4DKykouvfRSfvWrX3HhhRdSWVnJtm3b0gr0RPcywmskldfHmDFjsFgsNDY2xrw+/vSnP3HBBRewa9cuRo0alfQ+Mn19BINBRowYwTPPPENJSQkbN27kW9/6FhUVFXzpS18C4MEHH2TFihX84he/YMmSJTQ1NfHmm2/qx1988cW43W7+8pe/MH78eD7++ON2o8GFEMk99uYBthxs4NVd1Vx0woi+Xk5a0gpe+prFpE7XCcp7thCDXkFBATabDYfDQXl5OQA7d+4E4Pbbb2fhwoX6dYuLi5k9e7b+9e23384///lP/vOf//Dd73436X1ceeWVXHrppQDceeed/O53v+Odd97hvPPOS2utd999N+eccw4//elPAZg0aRIff/wxd911F1deeSWHDh0iJyeH888/n7y8PEaPHs0JJ5wAqMGL3+/n85//PCNHjqS4uJj58+cbakCC6H2pvD6CwSBOpzPm9w3gZz/7Gc899xzPP/98TLYjXqavD6vVGrOFwtixY9m0aRN///vf9eDl5z//OT/84Q+57rrr9OuddNJJALzyyits2bKF7du3M2XKFADGjRuX8nMjhABXm5pxqWxq6+OVpM9QwYs5/GYdkOBFiB6TbTXz8e2LU7puMBjE5XSRl5/XLSfT2dbu+eR03rx5MV83Nzdz66238sILL+jBQGtrK4cOHerwdmbNmqX/Oycnh/z8fI4fP572enbs2MGFF14Yc9mpp57KPffcQyAQYOHChYwePZpx48Zx3nnncd555+nlOLNnz+acc85h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",
      "text/plain": [
       "<Figure size 1000x500 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "#画线要注意的是损失是不一定在零到1之间的\n",
    "def plot_learning_curves(record_dict, sample_step=500):\n",
    "    # build DataFrame\n",
    "    train_df = pd.DataFrame(record_dict[\"train\"]).set_index(\"step\").iloc[::sample_step]\n",
    "    val_df = pd.DataFrame(record_dict[\"val\"]).set_index(\"step\")\n",
    "\n",
    "    # plot\n",
    "    fig_num = len(train_df.columns)\n",
    "    fig, axs = plt.subplots(1, fig_num, figsize=(5 * fig_num, 5))\n",
    "    for idx, item in enumerate(train_df.columns):    \n",
    "        axs[idx].plot(train_df.index, train_df[item], label=f\"train_{item}\")\n",
    "        axs[idx].plot(val_df.index, val_df[item], label=f\"val_{item}\")\n",
    "        axs[idx].grid()\n",
    "        axs[idx].legend()\n",
    "        # axs[idx].set_xticks(range(0, train_df.index[-1], 5000))\n",
    "        # axs[idx].set_xticklabels(map(lambda x: f\"{int(x/1000)}k\", range(0, train_df.index[-1], 5000)))\n",
    "        axs[idx].set_xlabel(\"step\")\n",
    "    \n",
    "    plt.show()\n",
    "\n",
    "plot_learning_curves(record, sample_step=100)  #横坐标是 steps"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 评估"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "ExecutionIndicator": {
     "show": true
    },
    "execution": {
     "iopub.execute_input": "2025-02-03T12:20:18.660997Z",
     "iopub.status.busy": "2025-02-03T12:20:18.660493Z",
     "iopub.status.idle": "2025-02-03T12:20:19.743187Z",
     "shell.execute_reply": "2025-02-03T12:20:19.741901Z",
     "shell.execute_reply.started": "2025-02-03T12:20:18.660962Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "loss:     0.9838\n",
      "accuracy: 0.6600\n"
     ]
    }
   ],
   "source": [
    "# dataload for evaluating\n",
    "\n",
    "# load checkpoints\n",
    "model.load_state_dict(torch.load(\"checkpoints/vgg/best.ckpt\", weights_only=True, map_location=\"cpu\"))\n",
    "\n",
    "model.eval()\n",
    "loss, acc = evaluating(model, eval_dl, loss_fct)\n",
    "print(f\"loss:     {loss:.4f}\\naccuracy: {acc:.4f}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 推理"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "e38f2f7bc3894c058cedbbae7fd3d9fe",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "  0%|          | 0/4688 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>filepath</th>\n",
       "      <th>class</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>cifar-10/test/1.png</td>\n",
       "      <td>deer</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>cifar-10/test/2.png</td>\n",
       "      <td>airplane</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>cifar-10/test/3.png</td>\n",
       "      <td>truck</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>cifar-10/test/4.png</td>\n",
       "      <td>ship</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>cifar-10/test/5.png</td>\n",
       "      <td>bird</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "              filepath     class\n",
       "0  cifar-10/test/1.png      deer\n",
       "1  cifar-10/test/2.png  airplane\n",
       "2  cifar-10/test/3.png     truck\n",
       "3  cifar-10/test/4.png      ship\n",
       "4  cifar-10/test/5.png      bird"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# test_df\n",
    "test_ds = Cifar10Dataset(\"test\", transform=transforms_eval)\n",
    "test_dl = DataLoader(test_ds, batch_size=batch_size, shuffle=False, drop_last=False)\n",
    "\n",
    "preds_collect = []\n",
    "model.eval()\n",
    "for data, fake_label in tqdm(test_dl):\n",
    "    data = data.to(device=device)\n",
    "    logits = model(data)\n",
    "    preds = [test_ds.idx_to_label[idx] for idx in logits.argmax(axis=-1).cpu().tolist()]\n",
    "    preds_collect.extend(preds)\n",
    "    \n",
    "test_df[\"class\"] = preds_collect\n",
    "test_df.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 导出 submission.csv\n",
    "test_df.to_csv(\"submission.csv\", index=False)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.14"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
